home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / CopyBits Demo 1.5 / CopyBits Demo.c < prev    next >
C/C++ Source or Header  |  1995-05-18  |  34KB  |  1,217 lines

  1. //
  2. // CopyBits Demo.c
  3. //
  4. // Written in May 1995 using Metrowerks CodeWarrior v5.
  5. //
  6. // The CopyBits Demo project v1.5 by Kenneth Worley.
  7. // This code is Copyright 1995. All Rights Reserved.
  8. // You may use this code in any project of your own. You may also
  9. // redistribute this project to anyone else as long as 1) all the
  10. // project files (including documentation files) are kept together,
  11. // and 2) nothing is charged for the project.
  12. //
  13. // Please ask my permission before including this code on any
  14. // disk or CD for sale.
  15. //
  16. // This is a tutorial project that demonstrates the use of
  17. // CopyBits and offscreen Graphics Worlds (GWorlds). It shows
  18. // several instances of copying images to and from offscreen
  19. // graphics worlds using CopyBits, provides several macros that
  20. // make the process a little easier, shows an example of
  21. // drawing into an offscreen graphics world and how that can
  22. // improve onscreen animation, and it demonstrates a "fade"
  23. // using CopyBits to fade a portion of the screen to black.
  24. //
  25. // Version 1.5 adds a fade from image to image using CopyBits.
  26. // Cool!
  27. //
  28. // In a nutshell, the app puts up a dialog that shows two
  29. // "source" pictures (1 & 2) and a destination area. Clicking
  30. // on the "Copy" buttons above each of the source pictures
  31. // causes the picture to be copied to the destination area.
  32. // Picture 1 is labelled as "flickery animation" and picture 2
  33. // is labelled as "smooth animation." When you put the cursor
  34. // over each picture, you should see a difference in how a
  35. // colored circle is animated over the picture. You should
  36. // occasionally see the background "flicker" through on
  37. // picture 1, but not on picture 2 because it uses an
  38. // intermediary GWorld to draw in.
  39. //
  40. // The Fade to black button over the destination area will
  41. // cause that area to fade to black from whatever happens to
  42. // be in there. It does this by repeatedly copying a gray
  43. // rectangle from a GWorld into the destination area using
  44. // the subPin transfer mode of CopyBits. The erase button
  45. // erases the destination area.
  46. //
  47. // The Fade picture buttons cause the picture under the
  48. // button to be faded into whatever is currently in the
  49. // destination area. This is accomplished using CopyBits'
  50. // blend transfer mode.
  51. //
  52. // Questions? Comments? Praise? Criticism? Jobs? Send them
  53. // all to me at KNEworley@aol.com. I'm a 'freelance'
  54. // programmer. I work on applications as well as low-level
  55. // software (control panels, INITs, etc.)
  56. //
  57.  
  58. #include <QDOffscreen.h>
  59.  
  60. // ===================== DEFINES =====================
  61.  
  62. #define kMainDlgID                128
  63.  
  64. #define kPict1ID                128
  65. #define kPict2ID                129
  66.  
  67. #define kMainDlgQuit            1
  68. #define kMainDlgCopyOne            2
  69. #define kMainDlgFadeOne            3
  70. #define kMainDlgCopyTwo            4
  71. #define kMainDlgFadeTwo            5
  72. #define kMainDlgErase            6
  73. #define kMainDlgFadeToBlack        7
  74. #define kMainDlgPICTOne            8
  75. #define kMainDlgPICTTwo            9
  76. #define kMainDlgDestPICT        10
  77. #define kMainDlgFadeSpeed        15
  78.  
  79. #define kSys7DlgID                129
  80. #define kMemoryOutDlgID            130
  81. #define kGWorldErrDlgID            131
  82. #define kNotYetImplDlgID        132
  83.  
  84. #define kAlertOKButton            1
  85.  
  86. // ===================== MACROS =====================
  87.  
  88. // w can be a WindowPtr, a DialogPtr, or a GWorldPtr. In any of these
  89. // cases, it returns the correct "bitmap" to send to CopyBits in the
  90. // source or destination bitmap parameter.
  91. #define WINBITMAP(w)        ((((WindowPeek)(w))->port).portBits)
  92.  
  93. // w can also be a WindowPtr, DialogPtr, or GWorldPtr for any of these.
  94. #define WINPORTRECT(w)        ((((WindowPeek)(w))->port).portRect)
  95. #define WINDOWWIDTH(w)        (WINPORTRECT(w).right - WINPORTRECT(w).left)
  96. #define WINDOWHEIGHT(w)        (WINPORTRECT(w).bottom - WINPORTRECT(w).top)
  97. #define WINCONTENTRECT(w)    ((**((WindowPeek)(w))->contRgn).rgnBBox)
  98. #define WINCONTENTRGN(w)    (((WindowPeek)(w))->contRgn)
  99. #define WINVISIBLERGN(w)    (((WindowPeek)(w))->port.visRgn)
  100. #define WINSTRUCTRECT(w)    ((**((WindowPeek)(w))->strucRgn).rgnBBox)
  101. #define WINSTRUCTRGN(w)        (((WindowPeek)(w))->strucRgn)
  102. #define WINUPDATERECT(w)    ((**((WindowPeek)(w))->updateRgn).rgnBBox)
  103. #define WINUPDATERGN(w)        (((WindowPeek)(w))->updateRgn)
  104.  
  105. // This takes a GDHandle (device handle)
  106. #define PIXELDEPTH(x)        ((**((**(x)).gdPMap)).pixelSize)
  107.  
  108. // r is a Rect
  109. #define TOPLEFT(r)            (* (Point*) &(r.top) )
  110. #define BOTRIGHT(r)            (* (Point*) &(r.bottom) )
  111.  
  112. // ===================== PROTOTYPES =====================
  113.  
  114. void main( void );
  115. void InitToolbox( void );
  116.  
  117. short    GetSysVersion( void );
  118. Boolean    HasColorQuickDraw( void );
  119.  
  120. Handle    GetItemHandle( DialogPtr dlg, short itemNo );
  121. void    GetItemRect( DialogPtr dlg, short itemNo, Rect *aRect );
  122. long    GetDialogNumberField( DialogPtr dlg, short itemNo );
  123.  
  124. void    DoAlert( short dlgID, Boolean playAlert );
  125.  
  126. GDHandle WindowDevice( WindowPtr winPtr );
  127.  
  128. void FadeToBlack( WindowPtr destWin, Rect *destRect, short fadeSpeed );
  129.  
  130. void FadeToImage( GWorldPtr srcImage, Rect *srcRect,
  131.     WindowPtr destWin, Rect *destRect, short fadeSpeed );
  132.  
  133. void FlickerAnimate( GWorldPtr srcPict, Rect *destRect );
  134. void SmoothAnimate( GWorldPtr srcPict, Rect *destRect );
  135.  
  136. // ===================== FUNCTIONS =====================
  137.  
  138. void main( void )
  139. {
  140.     DialogPtr        mainDlg;
  141.     DialogPtr        aDlg;
  142.     Rect            pict1Rect;
  143.     Rect            pict2Rect;
  144.     Rect            destRect;
  145.     PenState        savedPen;
  146.     short            itemHit;
  147.     RGBColor        savedFore;
  148.     RGBColor        savedBack;
  149.     RGBColor        blackColor;
  150.     RGBColor        whiteColor;
  151.     short            fadeSpeed;
  152.     OSErr            myErr;
  153.     Rect            pict1GRect;
  154.     Rect            pict2GRect;
  155.     GWorldPtr        pict1GWorld;
  156.     GWorldPtr        pict2GWorld;
  157.     EventRecord        myEvent;
  158.     
  159.     // Make colors for use later.
  160.     
  161.         blackColor.red = blackColor.green = blackColor.blue = 0;
  162.         whiteColor.red = whiteColor.green = whiteColor.blue = 0xFFFF;
  163.     
  164.     // Initialize the Mac Toolbox
  165.     
  166.     InitToolbox();
  167.     
  168.     // Check the system version. This project requires System 7
  169.     // or later for the GWorlds.
  170.     
  171.     if ( GetSysVersion() < 7 )
  172.     {
  173.         DoAlert( kSys7DlgID, true );
  174.         ExitToShell();
  175.     }
  176.     
  177.     // Load the main (only) dialog and display it on screen. This
  178.     // automatically displays our two source PICTS. Since there is
  179.     // a 'dctb' (Dialog Color Table) resource with the same ID
  180.     // as the dialog, the Dialog Manager uses NewColorDialog to
  181.     // make the dialog, thus giving us a color drawing port.
  182.     
  183.     mainDlg = GetNewDialog( kMainDlgID, NULL, (WindowPtr)(-1L) );
  184.     if ( !mainDlg )
  185.     {
  186.         DoAlert( kMemoryOutDlgID, true );
  187.         ExitToShell();
  188.     }
  189.     
  190.     // Select the text in the fade speed text edit box.
  191.     
  192.     SelectDialogItemText( mainDlg, kMainDlgFadeSpeed, 0, 255 );
  193.     
  194.     // Make sure the dialog is visible.
  195.     
  196.     ShowWindow( mainDlg );
  197.     SetGWorld( (CGrafPtr)mainDlg, NULL );
  198.     PenNormal();
  199.     
  200.     // Get the picture rectangles and the destination picture's
  201.     // rectangle. Save them for later use.
  202.     
  203.     GetItemRect( mainDlg, kMainDlgPICTOne, &pict1Rect );
  204.     GetItemRect( mainDlg, kMainDlgPICTTwo, &pict2Rect );
  205.     GetItemRect( mainDlg, kMainDlgDestPICT, &destRect );
  206.     
  207.     // Draw a box around each of the picture areas.
  208.     
  209.     {
  210.         Rect        tempRect;
  211.         
  212.         tempRect = destRect;
  213.         InsetRect( &tempRect, -1, -1 );
  214.         FrameRect( &tempRect );
  215.         tempRect = pict1Rect;
  216.         InsetRect( &tempRect, -1, -1 );
  217.         FrameRect( &tempRect );
  218.         tempRect = pict2Rect;
  219.         InsetRect( &tempRect, -1, -1 );
  220.         FrameRect( &tempRect );
  221.     }
  222.     
  223.     // Draw each of the two "source" pictures into an offscreen
  224.     // Graphics World for use later. For example, when the user
  225.     // passes the cursor over the left picture, regular animation
  226.     // techniques are used to make a circle "float" above the
  227.     // picture (causes flicker). This involves repeatedly
  228.     // copying the original picture to the window, then drawing
  229.     // the circle over it. When the user passes the cursor over
  230.     // the right picture, the original picture is drawn to an
  231.     // offscreen gworld and the circle is drawn over it, then
  232.     // that image is drawn to the window resulting in flicker-free
  233.     // animation of the floating circle.
  234.     //
  235.     // We need the two gworlds here so we have the original
  236.     // pictures to copy from.
  237.     
  238.         // Convert the destination rectangles into global coordinates
  239.     
  240.         pict1GRect = pict1Rect;
  241.         LocalToGlobal( &TOPLEFT(pict1GRect) );
  242.         LocalToGlobal( &BOTRIGHT(pict1GRect) );
  243.  
  244.         pict2GRect = pict2Rect;
  245.         LocalToGlobal( &TOPLEFT(pict2GRect) );
  246.         LocalToGlobal( &BOTRIGHT(pict2GRect) );
  247.  
  248.         // Create offscreen GWorlds with the same size and depth as the
  249.         // destination rectangles. The parameters are as follows:
  250.         //        &pictxGWorld        ptr to new graphics world
  251.         //        0                    bit depth same as graphics device of dest rect
  252.         //        &pictxGRect            bounds rectangle of GWorld
  253.         //        NULL                handle to a color table record - NULL means
  254.         //                            use the default record for that depth
  255.         //        NULL                handle to a graphics device record - we aren't
  256.         //                            creating a new graphics device
  257.         //        0L                    no flags
  258.     
  259.         myErr = NewGWorld( &pict1GWorld, 0, &pict1GRect, NULL, NULL, 0L );
  260.         myErr = NewGWorld( &pict2GWorld, 0, &pict2GRect, NULL, NULL, 0L );
  261.         
  262.         // I'm just assuming here that an error means we don't have enough memory.
  263.     
  264.         if ( myErr || !pict1GWorld || !pict2GWorld )
  265.         {
  266.             DoAlert( kMemoryOutDlgID, true );
  267.             ExitToShell();
  268.         }
  269.         
  270.         // Draw the pictures in the picture offscreen graphics worlds.
  271.         // When I read in the PICT resources, I'm just assuming there won't
  272.         // be any errors because they're already loaded by the dialog
  273.         // anyway, but you never know. You probably really should check :)
  274.         // See the FadeToBlack routine for more explanation on locking and
  275.         // unlocking pixels.
  276.         {
  277.             PicHandle        aPict;
  278.             PixMapHandle    thePixMap;
  279.             
  280.             // get and draw picture 1
  281.             
  282.             thePixMap = GetGWorldPixMap( pict1GWorld );
  283.             LockPixels( thePixMap );
  284.             SetGWorld( (CGrafPtr)pict1GWorld, NULL );
  285.             aPict = GetPicture( kPict1ID );
  286.             DrawPicture( aPict, &WINPORTRECT( pict1GWorld ) );
  287.             UnlockPixels( thePixMap );
  288.             
  289.             // get and draw picture 2
  290.             
  291.             thePixMap = GetGWorldPixMap( pict2GWorld );
  292.             LockPixels( thePixMap );
  293.             SetGWorld( (CGrafPtr)pict2GWorld, NULL );
  294.             aPict = GetPicture( kPict2ID );
  295.             DrawPicture( aPict, &WINPORTRECT( pict2GWorld ) );
  296.             UnlockPixels( thePixMap );
  297.             
  298.             // make sure and restore the dialog as the current port
  299.             
  300.             SetGWorld( (CGrafPtr)mainDlg, NULL );
  301.         }
  302.         
  303.     // Now wait for the user to press a button in the dialog
  304.     
  305.     itemHit = -1;
  306.     while ( itemHit != kMainDlgQuit )
  307.     {
  308.         WaitNextEvent( everyEvent, &myEvent, GetCaretTime(), NULL );
  309.         
  310.         // Check for disk events (bad disk mount).
  311.         
  312.         if ( (myEvent.what == diskEvt) &&
  313.             (HiWord(myEvent.message) != noErr) )
  314.         {
  315.             Point        dlgUpLeftCorner = { 100, 80 };    // ignored in Sys 7
  316.             
  317.             DIBadMount( dlgUpLeftCorner, myEvent.message );    // ignore result
  318.         }
  319.         // If we get an update event, redraw the squares around the
  320.         // picture areas.
  321.         else if ( myEvent.what == updateEvt )
  322.         {
  323.             Rect        tempRect;
  324.             
  325.             tempRect = destRect;
  326.             InsetRect( &tempRect, -1, -1 );
  327.             FrameRect( &tempRect );
  328.             tempRect = pict1Rect;
  329.             InsetRect( &tempRect, -1, -1 );
  330.             FrameRect( &tempRect );
  331.             tempRect = pict2Rect;
  332.             InsetRect( &tempRect, -1, -1 );
  333.             FrameRect( &tempRect );
  334.         }
  335.         
  336.         // If the mouse is over one of the pictures, call a routine
  337.         // to animate a "floating circle" over the picture. A
  338.         // different method is used for each picture.
  339.         {
  340.             Point        mouseLoc;
  341.             
  342.             GetMouse( &mouseLoc );
  343.             
  344.             if ( PtInRect( mouseLoc, &pict1Rect ) )
  345.                 FlickerAnimate( pict1GWorld, &pict1Rect );
  346.             else if ( PtInRect( mouseLoc, &pict2Rect ) )
  347.                 SmoothAnimate( pict2GWorld, &pict2Rect );
  348.         }
  349.  
  350.         // Pass the event to DialogSelect which takes care of tracking
  351.         // controls and updating everything (except the destination
  352.         // area) for us.
  353.         
  354.         if ( DialogSelect( &myEvent, &aDlg, &itemHit ) )
  355.         {
  356.             // What we do here depends on what the user clicked
  357.             
  358.             switch( itemHit )
  359.             {
  360.                 case kMainDlgCopyOne:
  361.                 
  362.                     // Save the pen state and foreground/background colors
  363.                     
  364.                         GetPenState( &savedPen );
  365.                         GetForeColor( &savedFore );
  366.                         GetBackColor( &savedBack );
  367.                     
  368.                     // Set foreground color to black, background color to white
  369.                     
  370.                         RGBForeColor( &blackColor );
  371.                         RGBBackColor( &whiteColor );
  372.                         
  373.                     // Just use CopyBits to copy the PICT. We're actually
  374.                     // copying from one place on the dialog to another here.
  375.  
  376.                         CopyBits( &WINBITMAP( mainDlg ), &WINBITMAP( mainDlg ),
  377.                             &pict1Rect, &destRect, srcCopy, NULL );
  378.                     
  379.                     // Restore the saved pen state and colors
  380.                     
  381.                         SetPenState( &savedPen );
  382.                         RGBForeColor( &savedFore );
  383.                         RGBBackColor( &savedBack );
  384.                         
  385.                     break;
  386.     
  387.                 case kMainDlgFadeOne:
  388.                 
  389.                     // Select the fade speed.
  390.                     
  391.                     SelectDialogItemText( mainDlg, kMainDlgFadeSpeed, 0, 255 );
  392.                     
  393.                     // Get the fade speed from the dialog.
  394.                     
  395.                     fadeSpeed = GetDialogNumberField( mainDlg,
  396.                         kMainDlgFadeSpeed );
  397.                     
  398.                     // Call FadeToImage to fade picture 1 into the
  399.                     // destination area.
  400.                     
  401.                     FadeToImage( pict1GWorld, &WINPORTRECT( pict1GWorld ),
  402.                         mainDlg, &destRect, fadeSpeed );
  403.                     
  404.                     break;
  405.     
  406.                 case kMainDlgCopyTwo:
  407.                 
  408.                     // Save the pen state and foreground/background colors
  409.                     
  410.                         GetPenState( &savedPen );
  411.                         GetForeColor( &savedFore );
  412.                         GetBackColor( &savedBack );
  413.                     
  414.                     // Set foreground color to black, background color to white
  415.                     
  416.                         RGBForeColor( &blackColor );
  417.                         RGBBackColor( &whiteColor );
  418.                         
  419.                     // Just use CopyBits to copy the PICT from one place in
  420.                     // the window to another.
  421.                     
  422.                         CopyBits( &WINBITMAP( mainDlg ), &WINBITMAP( mainDlg ),
  423.                             &pict2Rect, &destRect, srcCopy, NULL );
  424.                     
  425.                     // Restore the saved pen state and colors
  426.                     
  427.                         SetPenState( &savedPen );
  428.                         RGBForeColor( &savedFore );
  429.                         RGBBackColor( &savedBack );
  430.                         
  431.                     break;
  432.     
  433.                 case kMainDlgFadeTwo:
  434.                 
  435.                     // Select the fade speed.
  436.                     
  437.                     SelectDialogItemText( mainDlg, kMainDlgFadeSpeed, 0, 255 );
  438.                     
  439.                     // Get the fade speed from the dialog.
  440.                     
  441.                     fadeSpeed = GetDialogNumberField( mainDlg,
  442.                         kMainDlgFadeSpeed );
  443.                     
  444.                     // Call FadeToImage to fade picture 2 into the
  445.                     // destination area.
  446.                     
  447.                     FadeToImage( pict2GWorld, &WINPORTRECT( pict2GWorld ),
  448.                         mainDlg, &destRect, fadeSpeed );
  449.                                         
  450.                     break;
  451.     
  452.                 case kMainDlgErase:
  453.                 
  454.                     // Erase the destination area.
  455.                     
  456.                     EraseRect( &destRect );
  457.                     
  458.                     break;
  459.     
  460.                 case kMainDlgFadeToBlack:
  461.                     
  462.                     // Select the fade speed.
  463.                     
  464.                     SelectDialogItemText( mainDlg, kMainDlgFadeSpeed, 0, 255 );
  465.                     
  466.                     // Get the fade speed from the dialog.
  467.                     
  468.                     fadeSpeed = GetDialogNumberField( mainDlg,
  469.                         kMainDlgFadeSpeed );
  470.                     
  471.                     // And call FadeToBlack to fade the destination rectangle.
  472.                     
  473.                     FadeToBlack( mainDlg, &destRect, fadeSpeed );
  474.  
  475.                     break;
  476.     
  477.                 case kMainDlgQuit:
  478.                 
  479.                     break;
  480.             }
  481.         }
  482.     }
  483. }
  484.  
  485. void InitToolbox( void )
  486. {
  487.     /* Initialize the Mac Toolbox Managers */
  488.     
  489.     InitGraf((Ptr) &qd.thePort);
  490.     InitFonts();
  491.     InitWindows();
  492.     InitMenus();
  493.     FlushEvents(everyEvent,0);
  494.     TEInit();
  495.     InitDialogs(0L);
  496.     InitCursor();
  497. }
  498.  
  499. // GetSysVersion returns 7 if the system version is 7 or later,
  500. // 6 if it's 6 or later (but not 7), and 0 if earlier than 6.
  501. //
  502. short    GetSysVersion( void )
  503. {
  504.     SysEnvRec        env;
  505.     
  506.     SysEnvirons( 2, &env );
  507.     
  508.     if ( env.systemVersion >= 0x0700 )
  509.         return 7;
  510.     else if ( env.systemVersion >= 0x0600 )
  511.         return 6;
  512.     else
  513.         return 0;
  514. }
  515.  
  516. // HasColorQuickDraw returns true if the Mac has color
  517. // QuickDraw.
  518. //
  519. Boolean HasColorQuickDraw( void )
  520. {
  521.     SysEnvRec        env;
  522.     
  523.     SysEnvirons( 2, &env );
  524.     
  525.     return env.hasColorQD;
  526. }
  527.  
  528. // GetItemHandle returns a handle to a dialog item given the
  529. // dialog ptr and the item number.
  530. //
  531. Handle GetItemHandle( DialogPtr dlg, short itemNo )
  532. {
  533.     short            itemType;
  534.     Handle            itemHandle;
  535.     Rect            itemRect;
  536.     
  537.     GetDialogItem( dlg, itemNo, &itemType, &itemHandle, &itemRect );
  538.     
  539.     return itemHandle;
  540. }
  541.  
  542. // GetItemRect returns the rectangle of a dialog item (in local
  543. // coordinates) given the dialog ptr, and the item number.
  544. //
  545. void    GetItemRect( DialogPtr dlg, short itemNo, Rect *aRect )
  546. {
  547.     short            itemType;
  548.     Handle            itemHandle;
  549.     Rect            itemRect;
  550.     
  551.     GetDialogItem( dlg, itemNo, &itemType, &itemHandle, &itemRect );
  552.     
  553.     (*aRect) = itemRect;
  554. }
  555.  
  556. // GetDialogNumberField gets the text in the field specified by itemNo
  557. // in the dialog specified by dlg and converts it into a number. The
  558. // number is returned as the function result.
  559. //
  560. long    GetDialogNumberField( DialogPtr dlg, short itemNo )
  561. {
  562.     Str255        theStr;
  563.     long        theNum;
  564.     
  565.     GetDialogItemText( GetItemHandle( dlg, itemNo ), theStr );
  566.     StringToNum( theStr, &theNum );
  567.     return theNum;
  568. }
  569.  
  570.  
  571. void    DoAlert( short dialogID, Boolean playAlert )
  572. {
  573.     /*    This routine displays an alert whose resource ID number is specified
  574.      *    in the parameter dialogID.  The dialog specified is expected to have
  575.      *    at least one button (usually labeled "OK") which will be the default
  576.      *    button and have an ID if 1.  When the user presses this button, the
  577.      *    alert will be dismissed.
  578.      */
  579.     
  580.     WindowPtr        currentPort;    /* store the current port here */
  581.     DialogPtr        theDlg;            /* to store our dialog in */
  582.     
  583.     short        itemType;        /* these 3 local variables are used to */
  584.     Handle        itemHandle;        /* manipulate items in the dialog.     */
  585.     Rect        itemRect;
  586.  
  587.     short        itemHit;        /* use with ModalDialog */
  588.  
  589.     // Save the current grafPort
  590.     
  591.         GetPort( ¤tPort );
  592.     
  593.     // Now, load our dialog resource and display it.
  594.     
  595.         // load the dialog resource
  596.         
  597.             theDlg = GetNewDialog( dialogID, NULL, (WindowPtr)-1L );
  598.         
  599.         // make the dialog the current port
  600.         
  601.             SetPort( theDlg );
  602.         
  603.         // if the parameter specifies, do a system beep
  604.         
  605.             if ( playAlert )
  606.                 SysBeep( 3 );
  607.         
  608.         // now make sure the dialog is visible
  609.         
  610.             ShowWindow( theDlg );
  611.     
  612.      /*    Get the OK button item and draw a bold border around it to show that
  613.       *    it's the default button.
  614.       */
  615.      
  616.          GetDItem( theDlg, kAlertOKButton, &itemType, &itemHandle, &itemRect );
  617.          InsetRect( &itemRect, -4, -4 );
  618.          PenSize( 3, 3 );
  619.          FrameRoundRect( &itemRect, 16, 16 );
  620.          PenSize( 1, 1 );
  621.      
  622.      //    Loop and call ModalDialog until the user presses the OK button
  623.      // This routine requires that whatever alert is shown, the button with
  624.      // id number kAlertOKButton is the one that should dismiss the dialog.
  625.      
  626.          ModalDialog( NULL, &itemHit );
  627.          while ( itemHit != kAlertOKButton )
  628.              ModalDialog( NULL, &itemHit );
  629.          
  630.      /*    Now get rid of the dialog and set the port back to the control panel */
  631.      
  632.          DisposDialog( theDlg );
  633.          SetPort( currentPort );
  634. }
  635.  
  636. // WindowDevice returns a handle to the graphics device record of
  637. // the monitor that the window specified is displayed on.
  638. //
  639. GDHandle WindowDevice( WindowPtr winPtr )
  640. {
  641.     GDHandle        theDevice;
  642.     Point            targetPt;
  643.     
  644.     targetPt = TOPLEFT( ((WindowPeek)winPtr)->port.portRect );
  645.     
  646.     theDevice = GetDeviceList();
  647.     while ( !PtInRect( targetPt, &((**theDevice).gdRect) ) )
  648.     {
  649.         theDevice = GetNextDevice( theDevice );
  650.         if ( theDevice == NULL )
  651.         {
  652.             theDevice = GetMainDevice();
  653.             break;
  654.         }
  655.     }
  656.     
  657.     return theDevice;
  658. }
  659.  
  660. // FadeToBlack uses CopyBits and an offscreen graphics world to
  661. // fade an area (specified by destRect) in a window (specified
  662. // by destWin) to black. It does this by creating an offscreen
  663. // graphics world, painting the GWorld gray, then repeatedly
  664. // copying the gray rectangle to the destination area using the
  665. // subPin transfer method of CopyBits. This causes the destination
  666. // to grow darker and darker.
  667. //
  668. void FadeToBlack( WindowPtr destWin, Rect *destRect, short fadeSpeed )
  669. {
  670.     CGrafPtr        savedPort;
  671.     GDHandle        savedDevice;
  672.     GWorldPtr        offscreenWorld;
  673.     Rect            offscreenRect;
  674.     OSErr            myErr;
  675.     PenState        savedPen;
  676.     RGBColor        savedFore;
  677.     RGBColor        savedBack;
  678.     PixMapHandle    thePixMap;
  679.     long            repetitions;
  680.     RGBColor        grayColor;
  681.     RGBColor        blackColor;
  682.     RGBColor        whiteColor;
  683.     
  684.     // Make the black and white colors
  685.     
  686.         blackColor.blue = blackColor.green = blackColor.red = 0;
  687.         whiteColor.blue = whiteColor.green = whiteColor.red = 0xFFFF;
  688.         
  689.     // Save the current port/device. This should be used instead
  690.     // of GetPort. Though savedPort is defined as a CGrafPtr,
  691.     // the routine may return a GrafPtr or pointer to a GWorld
  692.     // instead (GWorldPtr).
  693.     
  694.         GetGWorld( &savedPort, &savedDevice );
  695.  
  696.     // Set the current port to the destination window
  697.     
  698.         SetGWorld( (CGrafPtr)destWin, NULL );
  699.     
  700.     // Save the pen state and color info of the destination window
  701.     
  702.         GetPenState( &savedPen );
  703.         GetForeColor( &savedFore );
  704.         GetBackColor( &savedBack );
  705.     
  706.     // Make sure the foreground color of the destination window
  707.     // is black and the background color is white.
  708.     
  709.         RGBForeColor( &blackColor );
  710.         RGBBackColor( &whiteColor );
  711.         
  712.     // Convert the destination rectangle into global coordinates
  713.     
  714.         offscreenRect = (*destRect);
  715.         LocalToGlobal( &TOPLEFT(offscreenRect) );
  716.         LocalToGlobal( &BOTRIGHT(offscreenRect) );
  717.     
  718.     // Make the offscreen rectangle smaller. It doesn't matter since it's
  719.     // all the same color and it'll use up less memory.
  720.     
  721.         offscreenRect.right = offscreenRect.left + 2;
  722.         offscreenRect.bottom = offscreenRect.top + 2;
  723.         
  724.     // Create an offscreen GWorld with the same depth as the
  725.     // destination window/rectangle. The parameters are as follows:
  726.     //        &offscreenWorld        ptr to new graphics world
  727.     //        0                    bit depth same as graphics device of dest rect
  728.     //        &offscreenRect        bounds rectangle of my GWorld
  729.     //        NULL                handle to a color table record - NULL means
  730.     //                            use the default record for that depth
  731.     //        NULL                handle to a graphics device record - we aren't
  732.     //                            creating a new graphics device
  733.     //        0L                    no flags
  734.     
  735.         myErr = NewGWorld( &offscreenWorld, 0, &offscreenRect, NULL, NULL, 0L );
  736.     
  737.     // Make sure the GWorld was created
  738.     
  739.         if ( myErr )
  740.         {
  741.             DoAlert( kGWorldErrDlgID, true );
  742.             ExitToShell();
  743.         }
  744.     
  745.     // Make the GWorld the current port
  746.     
  747.         SetGWorld( offscreenWorld, NULL );
  748.         
  749.     // Get the GWorld's pixel map handle and lock the pixels while we're
  750.     // drawing in the GWorld. We have to do this because a GWorld actually
  751.     // holds its pixel map in a handle rather than a pointer. LockPixels
  752.     // makes sure the pixel map doesn't move. You should lock a GWorld's
  753.     // pixels before drawing to or copying from the GWorld (and unlock
  754.     // them afterward).
  755.     
  756.         thePixMap = GetGWorldPixMap( offscreenWorld );
  757.         LockPixels( thePixMap );
  758.     
  759.     // The speed of the fade determines what shade of gray we'll paint
  760.     // the offscreen GWorld. fadeSpeed can range from 0 to 20 with 20
  761.     // being the fastest.
  762.     {
  763.         unsigned short    maxValue=0xFFFF;
  764.         
  765.         if ( fadeSpeed <= 0 ) fadeSpeed = 1;    // make sure speed is not zero
  766.         if ( fadeSpeed > 20 ) fadeSpeed = 20;    // make sure not over 20
  767.         
  768.         grayColor.blue = (fadeSpeed * 625) + 8000;
  769.         grayColor.red = grayColor.green = grayColor.blue;
  770.         RGBForeColor( &grayColor );
  771.         repetitions = maxValue / grayColor.blue;
  772.     }
  773.  
  774.     // Paint it all gray
  775.     
  776.         PaintRect( &(offscreenWorld->portRect) );
  777.     
  778.     // Make the destination window the current port
  779.     
  780.         SetGWorld( (CGrafPtr)destWin, NULL );
  781.         
  782.     // Loop and use CopyBits to copy the gray rectangle into the
  783.     // destination window/rect using the subPin transfer method
  784.     // so that the picture gets darker and darker.
  785.     
  786.         while ( repetitions )
  787.         {
  788.             CopyBits( &WINBITMAP( offscreenWorld ), &WINBITMAP( destWin ),
  789.                 &(offscreenWorld->portRect), destRect, subPin, NULL );
  790.  
  791.             repetitions--;
  792.         }
  793.  
  794.     // Make the GWorld the current port
  795.     
  796.         SetGWorld( offscreenWorld, NULL );
  797.         
  798.     // Now we'll do a fast fade just to make sure everything went completely
  799.     // to black.
  800.     {
  801.         unsigned short    maxValue=0xFFFF;
  802.         
  803.         grayColor.blue = 20500;
  804.         grayColor.red = grayColor.green = grayColor.blue;
  805.         RGBForeColor( &grayColor );
  806.         repetitions = maxValue / grayColor.blue + 1;
  807.     }
  808.  
  809.     // Paint it all gray
  810.     
  811.         PaintRect( &(offscreenWorld->portRect) );
  812.     
  813.     // Make the destination window the current port
  814.     
  815.         SetGWorld( (CGrafPtr)destWin, NULL );
  816.         
  817.     // Loop and use CopyBits to copy the gray rectangle into the
  818.     // destination window/rect using the subPin transfer method
  819.     // so that the picture gets darker and darker.
  820.     
  821.         while ( repetitions )
  822.         {
  823.             CopyBits( &WINBITMAP( offscreenWorld ), &WINBITMAP( destWin ),
  824.                 &(offscreenWorld->portRect), destRect, subPin, NULL );
  825.  
  826.             repetitions--;
  827.         }
  828.  
  829.     // Unlock the pixels again.
  830.     
  831.         UnlockPixels( thePixMap );
  832.         
  833.     // Restore the saved port/device
  834.     
  835.         SetGWorld( savedPort, savedDevice );
  836.         
  837.     // Restore the pen state and color info of the destination window
  838.     
  839.         SetPenState( &savedPen );
  840.         RGBForeColor( &savedFore );
  841.         RGBBackColor( &savedBack );
  842.         
  843.     // Dispose of the GWorld
  844.     
  845.         DisposeGWorld( offscreenWorld );
  846. }
  847.  
  848.  
  849. // FadeToImage takes the source image and does a smooth fade in
  850. // the destination window and rect from the image that is currently
  851. // there to the source image. This is done using CopyBits with
  852. // the "blend" transfer mode.
  853. //
  854. void FadeToImage( GWorldPtr srcImage, Rect *srcRect,
  855.     WindowPtr destWin, Rect *destRect, short fadeSpeed )
  856. {
  857.     CGrafPtr            savedPort;
  858.     GDHandle            savedDevice;
  859.     RGBColor            savedFore, savedBack;
  860.     PenState            savedPen;
  861.     RGBColor            grayColor;
  862.     RGBColor            blackColor;
  863.     RGBColor            whiteColor;
  864.     float                speed;
  865.     unsigned short        lastColor;
  866.     
  867.     // Make sure speed is within our limits and set it appropriately.
  868.     // We want to end up with a range of 1.2 to 3.2 from the original
  869.     // 0 to 20 range.
  870.     
  871.     if ( fadeSpeed < 1 ) speed = 1.2;
  872.     else if ( fadeSpeed > 20 ) speed = 3.2;
  873.     else speed = 1.2 + (fadeSpeed/10);
  874.     
  875.     // Make the black and white colors
  876.     
  877.         blackColor.blue = blackColor.green = blackColor.red = 0;
  878.         whiteColor.blue = whiteColor.green = whiteColor.red = 0xFFFF;
  879.         
  880.     // Save the current port and device
  881.     
  882.         GetGWorld( &savedPort, &savedDevice );
  883.         
  884.     // Set the port to the destination window and save its
  885.     // pen state and fore/background color values.
  886.     
  887.         SetGWorld( (CGrafPtr)destWin, NULL );
  888.         GetPenState( &savedPen );
  889.         GetForeColor( &savedFore );
  890.         GetBackColor( &savedBack );
  891.     
  892.     // Make sure the port's fore and background colors are set
  893.     // correctly for CopyBits to work correctly.
  894.         
  895.         RGBForeColor( &blackColor );
  896.         RGBBackColor( &whiteColor );
  897.             
  898.     // Lock the pixels in the source image. This is required
  899.     // when copying from a GWorld.
  900.         
  901.         LockPixels( GetGWorldPixMap( srcImage ) );
  902.     
  903.     // Set the beginning blend weight. This will be multiplied by
  904.     // speed before being used the first time.
  905.     
  906.         grayColor.blue = grayColor.green = grayColor.red = 0x0600;
  907.         lastColor = 0;
  908.         
  909.     // Loop and copy the source image to the destination
  910.     // blending more and more of the source image in by using
  911.     // a lighter and lighter "blend weight" (set by the OpColor
  912.     // function).
  913.  
  914.         while ( grayColor.blue < 0xFFFF )
  915.         {
  916.             // Lighten the blend weight color.
  917.             
  918.             grayColor.blue *= speed;
  919.             
  920.             // Make sure the number didn't roll over.
  921.             
  922.             if ( grayColor.blue < lastColor )
  923.                 grayColor.blue = 0xFFFF;
  924.             
  925.             grayColor.red = grayColor.green = grayColor.blue;
  926.  
  927.             // Remember the number for next time.
  928.             
  929.             lastColor = grayColor.blue;
  930.             
  931.             // Set the new blend weight.
  932.             
  933.             OpColor( &grayColor );
  934.             
  935.             // copy the image
  936.             
  937.             CopyBits( &WINBITMAP( srcImage ),
  938.                 &WINBITMAP( destWin ),
  939.                 srcRect, destRect, blend, NULL );
  940.         }
  941.  
  942.     // Finally, just copy the source image to the destination
  943.     // window unchanged to make sure the transformation is
  944.     // complete.
  945.     
  946.         CopyBits( &WINBITMAP( srcImage ),
  947.             &WINBITMAP( destWin ),
  948.             srcRect, destRect, srcCopy, NULL );
  949.     
  950.     // Reset the blend weight to black.
  951.     // This is the "normal" weight.
  952.     
  953.     OpColor( &blackColor );
  954.     
  955.     // Unlock the pixels in the source image.
  956.     
  957.         UnlockPixels( GetGWorldPixMap( srcImage ) );
  958.         
  959.     // Restore the destination window's properties.
  960.     
  961.         SetPenState( &savedPen );
  962.         RGBForeColor( &savedFore );
  963.         RGBBackColor( &savedBack );
  964.         
  965.     // Restore the current port and device.
  966.     
  967.         SetGWorld( savedPort, savedDevice );
  968. }
  969.  
  970. // FlickerAnimate animates a colored circle floating over
  971. // a picture at the mouse location. It draws directly to
  972. // the screen resulting in a circle that the background
  973. // occasionally "flickers" through. Drawing will occur in
  974. // the current port.
  975. //
  976. void FlickerAnimate( GWorldPtr srcPict, Rect *destRect )
  977. {
  978.     Point        mouseLoc;
  979.     Rect        mouseRect;
  980.     RGBColor    savedFore, savedBack;
  981.     RGBColor    blackColor, whiteColor;
  982.     PixMapHandle    thePixMap;
  983.     RgnHandle        savedClip;
  984.     RGBColor    redColor;
  985.     long        ignore;
  986.         
  987.     // Make black and white colors.
  988.     
  989.     blackColor.blue = blackColor.green = blackColor.red = 0;
  990.     whiteColor.blue = whiteColor.green = whiteColor.red = 0xFFFF;
  991.     
  992.     // Make the color to draw the circle with.
  993.     
  994.     redColor.blue = redColor.green = 0;
  995.     redColor.red = 0xFFFF;
  996.     
  997.     // Save fore and background colors and make them
  998.     // fore-black and back-white.
  999.     
  1000.     GetForeColor( &savedFore ); GetBackColor( &savedBack );
  1001.     RGBForeColor( &blackColor ); RGBBackColor( &whiteColor );
  1002.     
  1003.     // Save the clipping region.
  1004.     
  1005.     savedClip = NewRgn();
  1006.     GetClip( savedClip );
  1007.  
  1008.     // Set the clipping region to just cover the picture.
  1009.     
  1010.     ClipRect( destRect );
  1011.  
  1012.     // Lock the pixel map of the src GWorld while copying from it.
  1013.     
  1014.     thePixMap = GetGWorldPixMap( srcPict );
  1015.     LockPixels( thePixMap );
  1016.  
  1017.     // Loop and draw the picture and circle repeatedly.
  1018.     
  1019.     do
  1020.     {
  1021.         // Get a mouse location.
  1022.         
  1023.             GetMouse( &mouseLoc );
  1024.         
  1025.         // Make a square around the mouse location to draw
  1026.         // the circle in.
  1027.         
  1028.             mouseRect.left = mouseRect.right = mouseLoc.h;
  1029.             mouseRect.top = mouseRect.bottom = mouseLoc.v;
  1030.             InsetRect( &mouseRect, -20, -20 );
  1031.     
  1032.         // Copy the picture to the window.
  1033.  
  1034.             CopyBits( &WINBITMAP( srcPict ),
  1035.                 &WINBITMAP( FrontWindow() ),
  1036.                 &WINPORTRECT( srcPict ),
  1037.                 destRect, srcCopy, NULL );
  1038.             
  1039.         // Draw the circle at the mouse location.
  1040.         
  1041.             RGBForeColor( &redColor );
  1042.             PaintOval( &mouseRect );
  1043.             RGBForeColor( &blackColor );
  1044.         
  1045.         // Delay for a fraction of a second here. If we
  1046.         // don't, the flickering is really, really bad.
  1047.         
  1048.             Delay( 2, &ignore );
  1049.             
  1050.     } while ( PtInRect( mouseLoc, destRect ) );
  1051.  
  1052.     // Draw the picture one more time to make sure the
  1053.     // circle is erased.
  1054.     
  1055.         CopyBits( &WINBITMAP( srcPict ),
  1056.             &WINBITMAP( FrontWindow() ),
  1057.             &WINPORTRECT( srcPict ),
  1058.             destRect, srcCopy, NULL );
  1059.  
  1060.     // Unlock the GWorld's pixel map.
  1061.     
  1062.         UnlockPixels( thePixMap );
  1063.  
  1064.     // Restore the clipping region.
  1065.     
  1066.         SetClip( savedClip );
  1067.     
  1068.     // Restore the saved fore and background colors.
  1069.     
  1070.         RGBForeColor( &savedFore );
  1071.         RGBBackColor( &savedBack );
  1072. }
  1073.  
  1074. // SmoothAnimate animates a colored circle floating over
  1075. // a picture at the mouse location. It draws to a GWorld
  1076. // then copies the GWorld to the screen. This results in
  1077. // flicker-free animation where the background never
  1078. // shows through the floating circle.
  1079. //
  1080. void SmoothAnimate( GWorldPtr srcPict, Rect *destRect )
  1081. {
  1082.     Point            mouseLoc;
  1083.     Rect            mouseRect;
  1084.     RGBColor        savedFore, savedBack;
  1085.     RGBColor        blackColor, whiteColor;
  1086.     PixMapHandle    srcPixMap, midPixMap;
  1087.     GWorldPtr        middleMan;
  1088.     CGrafPtr        thisWindow;
  1089.     GDHandle        thisDevice;
  1090.     RGBColor        purpleColor;
  1091.     OSErr            myErr;
  1092.     
  1093.     // Save the current window and device (this is the window
  1094.     // we're drawing into.)
  1095.     
  1096.     GetGWorld( &thisWindow, &thisDevice );
  1097.     
  1098.     // Make black and white colors.
  1099.     
  1100.     blackColor.blue = blackColor.green = blackColor.red = 0;
  1101.     whiteColor.blue = whiteColor.green = whiteColor.red = 0xFFFF;
  1102.     
  1103.     // Make the color to draw the circle with.
  1104.     
  1105.     purpleColor.blue = purpleColor.red = 0xFFFF;
  1106.     purpleColor.green = 0;
  1107.     
  1108.     // Save fore and background colors and make them
  1109.     // fore-black and back-white.
  1110.     
  1111.     GetForeColor( &savedFore ); GetBackColor( &savedBack );
  1112.     RGBForeColor( &blackColor ); RGBBackColor( &whiteColor );
  1113.     
  1114.     // Make a GWorld to draw the picture and circle into. The
  1115.     // onscreen image will be copied from this "middle man."
  1116.     // It is the same size and depth as the destination
  1117.     // on screen.
  1118.     //        &middleMan            ptr to new graphics world
  1119.     //        0                    bit depth same as graphics device of dest rect
  1120.     //        destRect            bounds rectangle of new GWorld
  1121.     //        NULL                handle to a color table record - NULL means
  1122.     //                            use the default record for that depth
  1123.     //        NULL                handle to a graphics device record - we aren't
  1124.     //                            creating a new graphics device
  1125.     //        0L                    no flags
  1126.     
  1127.         myErr = NewGWorld( &middleMan, 0, destRect, NULL, NULL, 0L );
  1128.  
  1129.     // Make sure the GWorld was created
  1130.     
  1131.         if ( myErr || !middleMan )
  1132.         {
  1133.             DoAlert( kGWorldErrDlgID, true );
  1134.             ExitToShell();
  1135.         }
  1136.     
  1137.     // Lock the pixel map of the src GWorld and the middleMan
  1138.     // GWorld while copying from them.
  1139.     
  1140.         srcPixMap = GetGWorldPixMap( srcPict );
  1141.         LockPixels( srcPixMap );
  1142.         midPixMap = GetGWorldPixMap( middleMan );
  1143.         LockPixels( midPixMap );
  1144.  
  1145.     // Loop and draw the pict and the circle repeatedly.
  1146.     
  1147.     do
  1148.     {
  1149.         // Get a mouse location and translate into coordinates
  1150.         // local to the middleMan GWorld.
  1151.         
  1152.             GetMouse( &mouseLoc );
  1153.             mouseLoc.h -= destRect->left;
  1154.             mouseLoc.v -= destRect->top;
  1155.  
  1156.         // Make a square around the mouse location to draw
  1157.         // the circle in.
  1158.         
  1159.             mouseRect.left = mouseRect.right = mouseLoc.h;
  1160.             mouseRect.top = mouseRect.bottom = mouseLoc.v;
  1161.             InsetRect( &mouseRect, -20, -20 );
  1162.     
  1163.         // Set the current port to middleMan.
  1164.         
  1165.             SetGWorld( (CGrafPtr)middleMan, NULL );
  1166.         
  1167.         // Copy the picture to the middleMan GWorld.
  1168.  
  1169.             CopyBits( &WINBITMAP( srcPict ),
  1170.                 &WINBITMAP( middleMan ),
  1171.                 &WINPORTRECT( srcPict ),
  1172.                 &WINPORTRECT( middleMan ), srcCopy, NULL );
  1173.         
  1174.         // Draw the circle at the mouse location.
  1175.         
  1176.             RGBForeColor( &purpleColor );
  1177.             PaintOval( &mouseRect );
  1178.             RGBForeColor( &blackColor );
  1179.         
  1180.         // Reset the current port to the destination window.
  1181.         
  1182.             SetGWorld( thisWindow, thisDevice );
  1183.         
  1184.         // Now copy the middleMan image to the destination
  1185.         // window on screen.
  1186.         
  1187.             CopyBits( &WINBITMAP( middleMan ),
  1188.                 &WINBITMAP( thisWindow ),
  1189.                 &WINPORTRECT( middleMan ),
  1190.                 destRect, srcCopy, NULL );
  1191.                 
  1192.     } while ( PtInRect( mouseLoc, &WINPORTRECT( middleMan ) ) );
  1193.  
  1194.     // Draw the picture one more time to make sure the
  1195.     // circle is erased.
  1196.     
  1197.         CopyBits( &WINBITMAP( srcPict ),
  1198.             &WINBITMAP( FrontWindow() ),
  1199.             &WINPORTRECT( srcPict ),
  1200.             destRect, srcCopy, NULL );
  1201.  
  1202.     // Unlock the GWorld's and middleMan's pixel maps.
  1203.     
  1204.         UnlockPixels( srcPixMap );
  1205.         UnlockPixels( midPixMap );
  1206.     
  1207.     // Dispose of the middleMan GWorld.
  1208.     
  1209.         DisposeGWorld( middleMan );
  1210.  
  1211.     // Restore the saved fore and background colors.
  1212.     
  1213.         RGBForeColor( &savedFore );
  1214.         RGBBackColor( &savedBack );
  1215. }
  1216.  
  1217.